package com.demo.gateway.filter;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 限流过滤器
 * 基于Redis实现分布式限流
 */
@Slf4j
@Component
public class RateLimitFilter implements GatewayFilter, Ordered {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 限流Lua脚本
    private static final String RATE_LIMIT_SCRIPT = 
            "local key = KEYS[1]\n" +
            "local limit = tonumber(ARGV[1])\n" +
            "local window = tonumber(ARGV[2])\n" +
            "local current = redis.call('GET', key)\n" +
            "if current == false then\n" +
            "    redis.call('SET', key, 1)\n" +
            "    redis.call('EXPIRE', key, window)\n" +
            "    return {1, limit}\n" +
            "else\n" +
            "    local count = tonumber(current)\n" +
            "    if count < limit then\n" +
            "        redis.call('INCR', key)\n" +
            "        return {count + 1, limit}\n" +
            "    else\n" +
            "        return {count, limit}\n" +
            "    end\n" +
            "end";

    private final DefaultRedisScript<List> rateLimitScript;

    public RateLimitFilter() {
        this.rateLimitScript = new DefaultRedisScript<>();
        this.rateLimitScript.setScriptText(RATE_LIMIT_SCRIPT);
        this.rateLimitScript.setResultType(List.class);
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().value();
        String clientIp = getClientIp(request);
        
        // 根据不同路径设置不同的限流策略
        RateLimitConfig config = getRateLimitConfig(path);
        
        // 构建限流key
        String rateLimitKey = buildRateLimitKey(clientIp, path, config.getKeyType());
        
        try {
            // 执行限流检查
            List<Long> result = redisTemplate.execute(
                    rateLimitScript,
                    Arrays.asList(rateLimitKey),
                    config.getLimit(),
                    config.getWindow()
            );
            
            if (result != null && result.size() >= 2) {
                Long currentCount = result.get(0);
                Long limitCount = result.get(1);
                
                log.debug("限流检查 - IP: {}, Path: {}, Current: {}, Limit: {}", 
                        clientIp, path, currentCount, limitCount);
                
                if (currentCount <= limitCount) {
                    // 未超过限制，继续处理
                    return chain.filter(exchange);
                } else {
                    // 超过限制，返回限流响应
                    return handleRateLimit(exchange, currentCount, limitCount);
                }
            } else {
                log.warn("限流脚本执行异常，允许请求通过");
                return chain.filter(exchange);
            }
            
        } catch (Exception e) {
            log.error("限流检查异常，IP: {}, Path: {}", clientIp, path, e);
            // 异常情况下允许请求通过，避免影响正常业务
            return chain.filter(exchange);
        }
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIp(ServerHttpRequest request) {
        String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeaders().getFirst("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }
        
        return request.getRemoteAddress() != null ? 
                request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
    }

    /**
     * 根据路径获取限流配置
     */
    private RateLimitConfig getRateLimitConfig(String path) {
        if (path.startsWith("/api/seckill") || path.startsWith("/api/product/seckill")) {
            // 秒杀接口：每秒最多10次请求
            return new RateLimitConfig(10, 1, "ip_path");
        } else if (path.startsWith("/api/order")) {
            // 订单接口：每秒最多20次请求
            return new RateLimitConfig(20, 1, "ip_path");
        } else if (path.startsWith("/api/user")) {
            // 用户接口：每秒最多30次请求
            return new RateLimitConfig(30, 1, "ip");
        } else {
            // 其他接口：每秒最多50次请求
            return new RateLimitConfig(50, 1, "ip");
        }
    }

    /**
     * 构建限流key
     */
    private String buildRateLimitKey(String clientIp, String path, String keyType) {
        switch (keyType) {
            case "ip_path":
                return String.format("rate_limit:ip_path:%s:%s", clientIp, path.replaceAll("/", "_"));
            case "ip":
                return String.format("rate_limit:ip:%s", clientIp);
            default:
                return String.format("rate_limit:default:%s", clientIp);
        }
    }

    /**
     * 处理限流响应
     */
    private Mono<Void> handleRateLimit(ServerWebExchange exchange, Long currentCount, Long limitCount) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
        response.getHeaders().add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
        
        Map<String, Object> result = new HashMap<>();
        result.put("success", false);
        result.put("message", "请求过于频繁，请稍后重试");
        result.put("code", 429);
        result.put("currentCount", currentCount);
        result.put("limitCount", limitCount);
        
        String responseBody = JSON.toJSONString(result);
        DataBuffer buffer = response.bufferFactory().wrap(responseBody.getBytes(StandardCharsets.UTF_8));
        
        log.warn("请求被限流 - Current: {}, Limit: {}", currentCount, limitCount);
        
        return response.writeWith(Mono.just(buffer));
    }

    @Override
    public int getOrder() {
        return -100; // 高优先级，在认证过滤器之前执行
    }

    /**
     * 限流配置类
     */
    private static class RateLimitConfig {
        private final int limit;    // 限制次数
        private final int window;   // 时间窗口（秒）
        private final String keyType; // key类型

        public RateLimitConfig(int limit, int window, String keyType) {
            this.limit = limit;
            this.window = window;
            this.keyType = keyType;
        }

        public int getLimit() {
            return limit;
        }

        public int getWindow() {
            return window;
        }

        public String getKeyType() {
            return keyType;
        }
    }
}